using System.Collections;
using System;
using UnityEngine;
using UnityEngine.AI;

public class PlayerController : MonoBehaviour
{
    Camera camera_main;
    int block_layer;
    int stone_layer;
    int treeLayer;
    Coroutine moveCoroutine;
    NavMeshAgent nav;
    Animator anim;
    [HideInInspector]
    public CanMoveBlock canMoveBlock;
    bool lastOverlapState;
    MaterialFloatAnimation floatAnimation;
    Renderer render;
    Coroutine pushCoroutine;
    [HideInInspector]
    public bool stoneFalling;

    AudioSource audioSource;

    private void Awake()
    {
        camera_main = Camera.main;
        block_layer = LayerMask.GetMask("Block");
        stone_layer = LayerMask.GetMask("Stone");
        treeLayer = LayerMask.GetMask("Tree");
        nav = GetComponent<NavMeshAgent>();
        nav.updateRotation = false;
        anim = GetComponent<Animator>();
        canMoveBlock = GetComponent<CanMoveBlock>();
        floatAnimation = GetComponent<MaterialFloatAnimation>();
        render = GetComponentInChildren<Renderer>();

        MessageManager.BindAction<bool>(MsgKey.NavmeshEnable, SetNavmeshable);
        MessageManager.BindAction(MsgKey.Win, GameWin);
        lastOverlapState = canMoveBlock.IsOverlap;

        audioSource = GetComponent<AudioSource>();

        controllerForward = Vector3.forward.normalized * 3;
        controllerRight = Vector3.right * 3;
        nav.enabled = false;
    }

    //private void Update()
    //{
    //    if (FunLib.IsClickUI()) return;
    //    if (GameManager.instance==null || GameManager.instance.PC)
    //    {
    //        Ray ray = camera_main.ScreenPointToRay(Input.mousePosition);
    //        if (Physics.Raycast(ray, out RaycastHit hit, 200, layer))
    //        {
    //            LevelManager.ChooseBlock = hit.collider.gameObject;
    //        }
    //        else
    //        {
    //            LevelManager.ChooseBlock = null;
    //        }
    //    }
    //    else
    //    {
    //        if (Input.touchCount >= 1)
    //        {
    //            Ray ray = camera_main.ScreenPointToRay(Input.mousePosition);
    //            if (Physics.Raycast(ray, out RaycastHit hit, 200, layer))
    //            {
    //                LevelManager.ChooseBlock = hit.collider.gameObject;
    //            }
    //            else
    //            {
    //                LevelManager.ChooseBlock = null;
    //            }
    //        }
    //        else
    //        {
    //            LevelManager.ChooseBlock = null;
    //        }
    //    }
    //    if (Input.GetMouseButtonUp(0))
    //    {
    //        print(LevelManager.ChooseBlock);
    //        if (LevelManager.ChooseBlock != null && nav.enabled)
    //        {
    //            GameManager.instance?.PlayerTouchAudio();
    //            if (LevelManager.ChooseBlock.tag == "Stone")
    //            {

    //            }
    //            else
    //            {
    //                StopPushStone();
    //                MoveTo(LevelManager.ChooseBlock.transform.position + Vector3.up * 1.5f);
    //            }
    //        }
    //    }
    //}


    public static bool IsMoving = false;
    private Vector3 controllerForward;
    private Vector3 controllerRight;

    private void Update()
    {
        if (IsMoving || LevelManager.instance.cameraMoving) return;

        if (Input.GetKeyDown(KeyCode.W))
        {
            PlayerMoveDir(0, 1);
        }
        if (Input.GetKeyDown(KeyCode.S))
        {
            PlayerMoveDir(0, -1);
        }
        if (Input.GetKeyDown(KeyCode.A))
        {
            PlayerMoveDir(-1, 0);
        }
        if (Input.GetKeyDown(KeyCode.D))
        {
            PlayerMoveDir(1, 0);
        }
    }

    void PlayerMoveDir(int x, int y)
    {
        switch(CameraController.cameralook)
        {
            case CameraLook.Orig:
                StartCoroutine(PlayerMoveCor(controllerForward * x + controllerRight * -y));
                break;
            case CameraLook.Top:
                StartCoroutine(PlayerMoveCor(controllerForward * y + controllerRight * x));
                break;
            case CameraLook.Right:
                StartCoroutine(PlayerMoveCor(controllerForward * x));
                break;
            case CameraLook.Forward:
                StartCoroutine(PlayerMoveCor(controllerRight * x));
                break;
        }
    }

    IEnumerator PlayerMoveCor(Vector3 dir)
    {
        if(Physics.Raycast(transform.position + Vector3.up, dir, out var hit, 3, stone_layer))
        {
            PushStone(dir, hit.transform);
        }
        else
        {
            anim.SetBool("Move", true);

            bool haveBarr = Physics.Raycast(transform.position + Vector3.up, dir, out var hit1, 3, block_layer | treeLayer);
            bool canStand = Physics.Raycast(transform.position - Vector3.up, dir, out var hit2, 3, block_layer);

            if (!haveBarr && canStand)
            {
                IsMoving = true;

                float moveTime = 0.2f;
                float t = 0;
                Vector3 OriPos = transform.position;

                Quaternion qua = Quaternion.LookRotation(dir, Vector3.up);
                while (t < moveTime)
                {
                    transform.rotation = Quaternion.Lerp(transform.rotation, qua, 25 * Time.deltaTime);
                    transform.position = OriPos + dir * t / moveTime;

                    t += Time.deltaTime;

                    yield return null;
                }
                transform.position = hit2.transform.position + Vector3.up * 1.5f;
                IsMoving = false;
            }

            anim.SetBool("Move", false);
            
            canMoveBlock.FindStander();
        }
    }

    void PushStone(Vector3 dir, Transform stone)
    {
        if (Physics.Raycast(stone.position + Vector3.up, dir, out var hit1, 3, block_layer | treeLayer)) return; 
        if (canMoveBlock.IsOverlap)
        {
            return;
        }
        //player.PushStone(FindStoneSidePos(), transform);
        pushCoroutine = StartCoroutine(PushStoneCor(dir, stone));
    }



    void GameWin()
    {
        Material material = Resources.Load<Material>("Player_M");
        render.material = material;

        enabled = false;
        floatAnimation.Play(2);
        TimeAction.Delay(1.5f, () => { gameObject.SetActive(false); });
        if(audioSource.enabled) audioSource.Play();
    }

    public void Stop()
    {
        canMoveBlock.FindStander();
        if (moveCoroutine != null)
        {
            StopCoroutine(moveCoroutine);
        }
        if(nav.enabled && gameObject.activeSelf)
            nav.destination = transform.position;
        anim.SetBool("Move", false);
        moveCoroutine = null;
    }

    public bool IsPushing()
    {
        return pushCoroutine != null;
    }

    //public void PushStone(Vector3 pos, Transform stone)
    //{
    //    if (Mathf.Abs(stone.position.y - 1.5f - transform.position.y) > 0.2f)
    //    {
    //        return;
    //    }
    //    if(canMoveBlock.IsOverlap)
    //    {
    //        return;
    //    }
    //    if(stone.GetComponent<PushStone>().GetStoneSpeed().magnitude>0.01f) return;

    //    Vector3 dir = (stone.position - pos).normalized;
    //    Ray ray = new Ray(stone.position, dir);
    //    if (Physics.Raycast(ray, 3, layer | treeLayer))
    //    {
    //        return;
    //    }

    //    if (pushCoroutine==null)
    //        pushCoroutine = StartCoroutine(PushStoneCor(pos, stone));
    //}

    void StopPushStone()
    {
        if (pushCoroutine != null)
        {
            StopCoroutine(pushCoroutine);
            pushCoroutine = null;
        }
    }

    IEnumerator PushStoneCor(Vector3 dir, Transform stone)
    {
        MessageManager.Run(MsgKey.PushStone, true);
        anim.SetBool("Move", true);
        transform.forward = dir.normalized;
        stone.GetComponent<PushStone>().PlayAudio();

        float moveTime = 1f;
        float t = 0;

        Vector3 playerOriPos = transform.position;
        Vector3 stoneOriPos  = stone.position;
        IsMoving = true;

        Quaternion qua = Quaternion.LookRotation(dir, Vector3.up);
        while (t < moveTime)
        {
            transform.rotation = Quaternion.Lerp(transform.rotation, qua, 25 * Time.deltaTime);
            transform.position = playerOriPos + dir * t / moveTime;
            stone.position     = stoneOriPos + dir * t / moveTime;

            t += Time.deltaTime;

            yield return null;
        }
        transform.position = playerOriPos + dir;
        stone.position     = stoneOriPos  + dir;

        anim.SetBool("Move", false);

        Ray rayDown = new Ray(stone.position, Vector3.down);
        if (!Physics.Raycast(rayDown, 1.6f, block_layer))
        {
            stone.GetComponent<PushStone>().UseGravity();
        }
        if (!Physics.Raycast(rayDown, 50, block_layer))
        {
            TimeAction.Delay(1, () =>
            {
                Destroy(stone.gameObject);
                //(UImanager.Instance as UImanager).ShowEndGame();
            });
        }
        IsMoving = false;
        pushCoroutine = null;

        MessageManager.Run(MsgKey.PushStone, false);
        stone.GetComponent<CanMoveBlock>().FindStander();
        canMoveBlock.FindStander();
    }

    //IEnumerator PushStoneCor(Vector3 pos, Transform stone)
    //{
    //    CanMoveBlock stoneBlock = stone.GetComponent<CanMoveBlock>();
    //    MoveTo(pos-Vector3.up*1.5f);
    //    while (moveCoroutine!=null)
    //    {
    //        yield return null;
    //    }

    //    MessageManager.Run(MsgKey.PushStone, true);

    //    nav.enabled = false;
    //    anim.SetBool("Move", true);
    //    Vector3 dir = (stone.position - pos).normalized;
    //    Vector3 targetStonePos = stone.position + dir * 3;
    //    Quaternion qua = Quaternion.LookRotation(dir);
    //    float speed = 2;
    //    bool hitBlock = false;


    //    stone.GetComponent<PushStone>().PlayAudio();
    //    while(Vector2.Distance(new Vector2(targetStonePos.x, targetStonePos.z),
    //        new Vector2(stone.position.x, stone.position.z))>0.1f)
    //    {
    //        Ray ray = new Ray(stone.position, dir);
    //        hitBlock = Physics.Raycast(ray, 1.5f, layer | treeLayer);
    //        if (hitBlock)
    //        {
    //            break;
    //        }
    //        transform.rotation = Quaternion.Lerp(transform.rotation, qua, Time.deltaTime * 8);
    //        stone.position += (dir * Time.deltaTime * speed);
    //        transform.position += (dir * Time.deltaTime * speed);
    //        yield return null;
    //    }
    //    if(!hitBlock)
    //    {
    //        stone.position = targetStonePos;
    //        Vector3 playPos = targetStonePos - dir * 3;
    //        playPos.y = transform.position.y;
    //        transform.position = playPos;
    //    }
    //    nav.enabled = true;
    //    anim.SetBool("Move", false);
    //    pushCoroutine = null;

    //    Ray rayDown = new Ray(stone.position, Vector3.down);
    //    if (!Physics.Raycast(rayDown, 1.6f, layer))
    //    {
    //        stone.GetComponent<PushStone>().UseGravity();
    //    }
    //    if (!Physics.Raycast(rayDown, 100, layer))
    //    {
    //        TimeAction.Delay(1, () =>{ 
    //            Destroy(stone.gameObject);
    //            //(UImanager.Instance as UImanager).ShowEndGame();
    //        });
    //    }

    //    MessageManager.Run(MsgKey.PushStone, false);
    //    stoneBlock.FindStander();
    //    canMoveBlock.FindStander();
    //}

    void MoveTo(Vector3 pos)
    {
        if (!gameObject.activeSelf)
            return;
        Stop();
        moveCoroutine = StartCoroutine(PlayerMove(pos));
    }

    IEnumerator PlayerMove(Vector3 pos)
    {
        nav.destination = pos;
        anim.SetBool("Move", true);
        while (true)
        {
            Debug.DrawRay(pos, Vector3.up*3, Color.blue);

            if (!nav.isOnNavMesh || !gameObject.activeSelf || !nav.enabled )
            {
                break;
            }
            if (nav.remainingDistance < 0.5f && !nav.pathPending)
            {
                break;
            }
            if (nav.path.corners.Length == 2)
            {
                break;
            }
            if (nav.desiredVelocity.magnitude > 0.1f)
            {
                transform.rotation = Quaternion.Lerp(transform.rotation,
Quaternion.LookRotation(nav.desiredVelocity.normalized), Time.deltaTime * 12);
            }

            yield return null;
        }
        while(true)
        {
            Vector3 dis = pos - transform.position;
            dis.y = 0;
            if(dis.magnitude<0.5f)
            {
                break;
            }
            transform.rotation = Quaternion.Lerp(transform.rotation,
Quaternion.LookRotation(dis.normalized), Time.deltaTime * 12);
            nav.Warp(transform.position + dis.normalized * nav.speed * Time.deltaTime*0.8f);
            yield return null;
        }
        Stop();
    }

    void SetNavmeshable(bool b)
    {
        if (!b)
        {
            if (moveCoroutine != null)
            {
                StopCoroutine(moveCoroutine);
            }
            if (nav.enabled && gameObject.activeSelf)
                nav.destination = transform.position;
            anim.SetBool("Move", false);
            moveCoroutine = null;
        }

        nav.enabled = b;
        if(canMoveBlock.IsOverlap)
        {
            nav.enabled = false;
        }
        if(lastOverlapState!= canMoveBlock.IsOverlap)
        {
            lastOverlapState = canMoveBlock.IsOverlap;
            if(lastOverlapState)
            {
                Material material = Resources.Load<Material>("Player_M");
                render.material = material;
                floatAnimation.Play(0);
            }
            else
            {
                floatAnimation.Play(1);
                TimeAction.Delay(0.28f, () => {
                    Material material = Resources.Load<Material>("Play_M1");
                    render.material = material;
                });
            }
        }
    }

    public void SetNavEnable(bool b)
    {
        Stop();
        nav.enabled=b;
    }

    public bool GetNavEnable()
    {
        return nav.enabled;
    }
}
